﻿using AutoMapper;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Guids;
using YaAppoint.Appointments.AutoAppoint;
using YaAppoint.Appointments.Pools;
using YaAppoint.CustomSettings;
using YaAppoint.ExamAppies;
using YaAppoint.ExamApplications;
using YaAppoint.Response;

namespace YaAppoint.Appointments.HisAppoint
{
    [RemoteService(isEnabled: false)]
    public class HisAppointAppService(IExamApplyAppService examApplyAppService,
                                      IAutoAppointAppService autoAppointAppService,
                                      IAppointmentAppService appointmentAppService,
                                      IConfiguration configuration,
                                      ICustomSettingAppService customSettingAppService,
                                      IGuidGenerator guidGenerator,
                                      IDistributedCache<Suggestion,Guid> cache,
                                      IMapper mapper,
                                      IRepository<Appointment,Guid> appointmentRepository,
                                      IRepository<Pool,Guid> poolRepository,
                                      IRepository<ExamApplication, Guid> examApplicationRepository) : ApplicationService, IHisAppointAppService, ITransientDependency
    {
        private readonly IExamApplyAppService _examApplyAppService = examApplyAppService;
        private readonly IRepository<ExamApplication, Guid> _examApplicationRepository = examApplicationRepository;
        private readonly IAutoAppointAppService _autoAppointAppService = autoAppointAppService;
        private readonly IAppointmentAppService _appointmentAppService = appointmentAppService;
        private readonly IMapper _mapper = mapper;
        private readonly IGuidGenerator _guidGenerator = guidGenerator;
        private readonly ICustomSettingAppService _customSettingAppService= customSettingAppService;
        private readonly IDistributedCache<Suggestion,Guid> _cache = cache;
        private readonly IRepository<Appointment, Guid> _appointmentRepository= appointmentRepository;
        private readonly IRepository<Pool, Guid> _poolRepository= poolRepository;
        private readonly IConfiguration _configuration = configuration;

        /// <summary>
        /// 获取已预约申请项目
        /// </summary>
        /// <param name="applyItems">申请单列表</param>
        public async Task<BackJson> GetAppointedApplyItems(string applyNumbers)
        {
            return await _examApplyAppService.GetApplyItemListAsync(applyNumbers, ApplyItemStatus.Appointed);
        }


        /// <summary>
        /// 获取未预约申请项目
        /// </summary>
        /// <param name="applyItems"></param>
        /// <returns></returns>
        public async Task<BackJson> GetNoAppointApplyItems(string applyNumbers)
        {
            return await _examApplyAppService.GetApplyItemListAsync(applyNumbers, ApplyItemStatus.UnAppointed);
        }

        /// <summary>
        /// 获取患者信息
        /// </summary>
        public async Task<BackJson> GetPatientInfo(string applyNumbers)
        {
            var res = new BackJson();
            var numberList = applyNumbers.Split(',').ToList();
            var list = await _examApplicationRepository.GetListAsync(ea=>numberList.Contains(ea.ApplyNumber));
            if (list.Count == 0)
            {
                res.code = 1;
                res.err = "所有申请单中不包含任何申请项目！";
            }
            var patients = list.DistinctBy(e => e.PatientId).ToList();
            if (patients.Count > 1)
            {
                res.code = 2;
                res.err = "申请单来自不同的患者！";
            }
            else
            {
                var patient = patients[0];
                res.code = 0;
                res.data = new
                {
                    name=patient.Name,
                    patientId=patient.PatientId,
                    patientType=patient.PatientType,
                    ageValue = patient.AgeValue,
                    ageUnit= patient.AgeUnit,
                    age=patient.Age,
                    sex=patient.Sex,
                    birthDate =patient.BirthDate!=null? patient.BirthDate.Value.ToString("yyyy-MM-dd"):null,
                    parentName=patient.ParentName,
                    idCardNumber=patient.IdCardNumber,
                    medicalInsuranceType=patient.MedicalInsuranceType,
                    medicalInsuranceNumber=patient.MedicalInsuranceNumber,
                    phoneNumber=patient.PhoneNumber,
                    ethnicGroup=patient.EthnicGroup,
                    isPregnancy=patient.IsPregnancy,
                    gestationalWeek=patient.GestationalWeek,
                    lastMenstruationDate=patient.LastMenstruationDate!=null?patient.LastMenstruationDate.Value.ToString("yyyy-MM-dd"):null,
                    LastMenstruationDateStr= patient.LastMenstruationDate != null ? patient.LastMenstruationDate.Value.ToString("yyyy-MM-dd"):null,
                    isEmergency=patient.IsEmergency
                };
            }
            return res;
        }


        /// <summary>
        /// 加载页面初始化信息
        /// </summary>
        public async Task<BackJson> GetInitInfo(string applyNumbers)
        {
            var res = new BackJson();
            var numberList = applyNumbers.Split(',').ToList();
            var list = await _examApplicationRepository.GetListAsync(ea => numberList.Contains(ea.ApplyNumber));
            object patientInfo = new object();
            if (list.Count == 0)
            {
                res.code = 1;
                res.err = "所有申请单中不包含任何申请项目！";
            }
            var patients = list.DistinctBy(e => e.PatientId).ToList();
            if (patients.Count > 1)
            {
                res.code = 2;
                res.err = "申请单来自不同的患者！";
            }
            else
            {
                var patient = patients[0];
                patientInfo = new
                {
                    name = patient.Name,
                    patientId = patient.PatientId,
                    patientType = patient.PatientType,
                    ageValue = patient.AgeValue,
                    ageUnit = patient.AgeUnit,
                    age = patient.Age,
                    sex = patient.Sex,
                    birthDate = patient.BirthDate != null ? patient.BirthDate.Value.ToString("yyyy-MM-dd") : null,
                    parentName = patient.ParentName,
                    idCardNumber = patient.IdCardNumber,
                    medicalInsuranceType = patient.MedicalInsuranceType,
                    medicalInsuranceNumber = patient.MedicalInsuranceNumber,
                    phoneNumber = patient.PhoneNumber,
                    ethnicGroup = patient.EthnicGroup,
                    isPregnancy = patient.IsPregnancy,
                    gestationalWeek = patient.GestationalWeek,
                    lastMenstruationDate = patient.LastMenstruationDate != null ? patient.LastMenstruationDate.Value.ToString("yyyy-MM-dd") : null,
                    LastMenstruationDateStr = patient.LastMenstruationDate != null ? patient.LastMenstruationDate.Value.ToString("yyyy-MM-dd") : null,
                    isEmergency = patient.IsEmergency
                };
            }

            var setting =await  _customSettingAppService.GetGlobalSetting();
            var hospitalName = _configuration!["InstitutionName"]!.ToString();
            res.code = 0;
            res.data = new
            {
                patient = patientInfo,
                hospital = hospitalName,
                globalSetting = setting
            };
            return res;
        }


        /// <summary>
        /// 获取推荐号段
        /// </summary>
        /// <param name="applyItemIds">申请项目Id集合</param>
        /// <param name="date">日期</param>
        /// <returns></returns>
        public async Task<BackJson> GetSuggestInterval(List<Guid> applyItemIds, DateTime date)
        {
            var res = new BackJson();
            try
            {
                List<Interval> intervals = new List<Interval>();
                var applyItems = await _examApplicationRepository.GetListAsync(e => applyItemIds.Contains(e.Id));
                var deviceTypes = applyItems.DistinctBy(e => e.DeviceTypeId).Select(e => e.DeviceType).ToList();
                if (deviceTypes.Count > 1)
                {
                    throw new BusinessException(CustomErrorCodes.MoreThanOneDeviceTypeFoundInApplyItems);
                }

                var pools = await _autoAppointAppService.GetSuggestedPools(_mapper.Map<List<ExamApplication>, List<ExamApplicationDto>>(applyItems), date.Date);
                Suggestion suggestion = new Suggestion();
                if (pools.Count > 0)
                {
                    suggestion.Id = _guidGenerator.Create();
                    intervals = await GetIntervals(pools, date,suggestion.Id);
                    suggestion.Intervals = intervals;
                    suggestion.CreationTime = DateTime.Now;
                    await _cache.SetAsync(suggestion.Id, suggestion, new DistributedCacheEntryOptions
                    {
                        AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(5)
                    });
                }
                else
                {
                    suggestion.Id = _guidGenerator.Create();
                    suggestion.Intervals = new List<Interval>();
                    suggestion.CreationTime = DateTime.Now;
                }
                res.code = 0;
                res.data = suggestion;
            }
            catch (Exception e)
            {
                res.code = 1;
                res.err = e.Message;
            }
            return res;
        }


        /// <summary>
        /// 整理号池生成固定号段
        /// </summary>
        /// <param name="pools">推荐的号池</param>
        /// <returns></returns>
        public async Task<List<Interval>> GetIntervals(List<PoolDto> pools,DateTime date,Guid suggestionId)
        {
            var allIntervals = new List<Interval>();
            var startTime = date.Date;
            var endTime =startTime.AddHours(24);
            var globalSetting = await _customSettingAppService.GetGlobalSetting();
            TimeSpan span = TimeSpan.FromDays(1);
            switch (globalSetting.HisIntervalSpan)
            {
                case HisIntervalSpan.HalfHour:
                    span = TimeSpan.FromMinutes(30);
                    break;


                case HisIntervalSpan.OneHour:
                    span = TimeSpan.FromHours(1);
                    break;

                case HisIntervalSpan.TwoHour:
                    span = TimeSpan.FromHours(2);
                    break;

                default:
                    span = TimeSpan.FromHours(1);
                    break;
            }

            while (startTime < endTime)
            { 
                var interval = new Interval();
                interval.Id = _guidGenerator.Create();
                interval.SuggestionId = suggestionId;
                interval.StartTime = startTime;
                interval.EndTime = interval.StartTime + span;
                interval.Date = date.Date;
                interval.Pools = new List<PoolDto>();
                allIntervals.Add(interval);
                startTime += span;
            }
            for (int i = 0; i < allIntervals.Count; i++)
            {
                foreach (var pool in pools)
                {
                    if (pool.StartTime >= allIntervals[i].StartTime && pool.StartTime < allIntervals[i].EndTime)
                    {
                        allIntervals[i].MaxCount += pool.MaxCount;
                        allIntervals[i].CurrentCount += pool.CurrentCount!.Value;
                        allIntervals[i].Pools.Add(pool);
                    }
                }
            }
            return allIntervals.Where(i=>i.Pools.Count>0).OrderBy(i=>i.StartTime).ToList();
        }

        /// <summary>
        /// 预约提交
        /// </summary>
        public async Task<BackJson> AppointSubmit(List<ApplyItemDto> applyItems, bool confirmed)
        {
            var res = new BackJson();
            var ids = applyItems.Select(a => a.Id).ToList();
            if (applyItems.DistinctBy(a => a.IntervalId).ToList().Count > 1)
            {
                Logger.LogError("预约中包含多个号段!请联系维护人员");
                res.code = 1;
                res.msg = "预约中包含多个号段";
                return res;
            }
            var applyItem = applyItems[0];
            var suggestion = await _cache.GetAsync(applyItem.SuggestionId);
            if (suggestion == null)
            {
                Logger.LogError("提交预约 推荐不存在或已过期");
                res.code = 1;
                res.msg = "推荐已过期，请重新推荐号段";
                return res;
            }
            await _cache.RemoveAsync(applyItem.SuggestionId);
            var interval = suggestion.Intervals.FirstOrDefault(i => i.Id == applyItem.IntervalId);
            if (interval == null)
            {
                Logger.LogError("前端选定号段在后端不存在，或者号段与推荐不匹配");
                res.code = 1;
                res.msg = "前端选定号段在后端不存在，或者号段与推荐不匹配";
            }
            if (interval!.Pools == null || interval.Pools.Count == 0)
            {
                Logger.LogError("号段中无任何有效号池!");
                res.code = 1;
                res.msg = "号段中无任何有效号池!";
            }
            var pool = interval!.Pools!.MinBy(p => (p.CurrentCount / p.MaxCount) * 100);
            var applyItemList = await _examApplicationRepository.GetListAsync(e => ids.Contains(e.Id));
            AppointmentStatus status = new AppointmentStatus();
            if (confirmed)
            {
                status = AppointmentStatus.Submit;
            }
            else
            {
                status = AppointmentStatus.Unconfirmed;
            }
            var appointment = await _appointmentAppService.CreateAppointment(_mapper.Map<List<ExamApplication>, List<ExamApplicationDto>>(applyItemList!), pool!, status);

            for (int i = 0; i < applyItemList.Count; i++)
            {
                applyItemList[i].AppointmentId = appointment.Id;
                applyItemList[i].AppointmentInfo = appointment.DisplayName;
            }
            await _examApplicationRepository.UpdateManyAsync(applyItemList);

            // 如果已确认方案则直接回写his
            if (confirmed)
            {
                List<ApplyExamItemStatusChangeDto> changeDtos = new List<ApplyExamItemStatusChangeDto>();
                foreach (var applyitem in applyItemList)
                { 
                    var dto = new ApplyExamItemStatusChangeDto();
                    dto.Name = applyitem.Name;
                    dto.PatientId = applyitem.PatientId;
                    dto.AppointmentInfo = appointment.DisplayName;
                    dto.ChangeType = ApplyExamItemStatusChangeType.Submit;
                    dto.ChangeTypeStr = "新建";
                    dto.AppointmentNo = appointment.Number;
                    dto.ApplyNumber = applyitem.ApplyNumber;
                    dto.ExamItemNumber = applyitem.ExamItemNumber;
                    dto.ExamItemCode = applyitem.ExamItemCode;
                    dto.ExamItemName = applyitem.ExamItemName;

                    changeDtos.Add(dto);
                }

                await _appointmentAppService.ApplyExamItemStatusChangeToHis(changeDtos);
            }
                
            if (confirmed)
            {
                return new BackJson()
                {
                    code = 0,
                    msg = "预约成功!"
                };
            }
            else
            { 
                return new BackJson()
                {
                    code=0,
                    msg="提交成功，目前预约方案待确认"
                };
            }
        }

        /// <summary>
        /// 预约修改
        /// </summary>
        public async Task<BackJson> EditAppoints(List<ApplyItemDto> applyItems,bool confirmed)
        {
            var res = new BackJson();
            var ids = applyItems.Select(e => e.Id).ToList();
            if (applyItems.DistinctBy(a => a.IntervalId).ToList().Count > 1)
            {
                Logger.LogError("预约中包含多个号段！请联系维护人员");
                res.code = 1;
                res.msg = "预约中包含多个号段";
                return res;
            }
            var suggestion = await _cache.GetAsync(applyItems[0].SuggestionId);
            if (suggestion == null)
            {
                Logger.LogError("推荐已过期");
                res.code = 1;
                res.msg = "推荐已过期，请重新推荐号段";
                return res;
            }
           
            var interval = suggestion.Intervals.FirstOrDefault(i => i.Id == applyItems[0].IntervalId);
            if (interval == null)
            {
                Logger.LogError("前端选定号段在后端不存在，或者号段与推荐不匹配");
                res.code = 1;
                res.msg = "前端选定号段在后端不存在，或者号段与推荐不匹配";
                return res;
            }
            if (interval!.Pools == null || interval.Pools.Count == 0)
            {
                Logger.LogError("号段中无任何有效号池！");
                res.code = 1;
                res.msg = "号段中无任何有效号池！";
                return res;
            }

            try
            {
                var pool = interval!.Pools!.MinBy(p => (p.CurrentCount / p.MaxCount) * 100);
                var applyItemList = await _examApplicationRepository.GetListAsync(e => ids.Contains(e.Id));
                AppointmentStatus status = new AppointmentStatus();
                
                for (int i = 0; i < applyItemList.Count; i++)
                {
                    var oldAppointment = applyItemList[i].Appointment;
                    oldAppointment!.ExamApplications.Remove(applyItemList[i]);
                    if (oldAppointment.ExamApplications.Count == 0)
                    {
                        oldAppointment.Pool.Current -= 1;
                        oldAppointment.Pool.Current = oldAppointment.Pool.Current < 0 ? 0 : oldAppointment.Pool.Current;
                        await _appointmentRepository.DeleteAsync(oldAppointment.Id);
                        await _poolRepository.UpdateAsync(oldAppointment.Pool);
                    }
                }
                var appointment = await _appointmentAppService.CreateAppointment(_mapper.Map<List<ExamApplication>, List<ExamApplicationDto>>(applyItemList), pool!, status);
                await _cache.RemoveAsync(applyItems[0].SuggestionId);
                // 如果已确认预约方案则直接回写
                if (confirmed)
                {
                    List<ApplyExamItemStatusChangeDto> changeDtos = new List<ApplyExamItemStatusChangeDto>();

                    foreach (var applyItem in applyItemList)
                    { 
                        var dto = new ApplyExamItemStatusChangeDto();
                        dto.Name = applyItem.Name;
                        dto.PatientId = applyItem.PatientId;
                        dto.AppointmentInfo = appointment.DisplayName;
                        dto.ChangeType = ApplyExamItemStatusChangeType.Edit;
                        dto.ChangeTypeStr = "修改预约";
                        dto.AppointmentNo = appointment.DisplayName;
                        dto.ApplyNumber = applyItem.ApplyNumber;
                        dto.ExamItemNumber = applyItem.ExamItemNumber;
                        dto.ExamItemCode = applyItem.ExamItemCode;
                        dto.ExamItemName = applyItem.ExamItemName;

                        changeDtos.Add(dto);
                    }

                    await _appointmentAppService.ApplyExamItemStatusChangeToHis(changeDtos);
                }
                res.code = 0;
                res.msg = "保存成功";
                return res;
            }
            catch (Exception e)
            {
                res.code = 1;
                res.msg = e.Message;
            }
            return res;
        }

        /// <summary>
        /// 预约取消
        /// </summary>
        public async Task<BackJson> Cancel(List<Guid> applyItemIds,bool confirmed)
        {
            var res = new BackJson();
            try
            {
                var applyItemList = await _examApplicationRepository.GetListAsync(e => applyItemIds.Contains(e.Id));
                for (int i = 0; i < applyItemList.Count; i++)
                {
                    var appointment = applyItemList[i].Appointment;
                    if (appointment != null && appointment.ExamApplications.Count > 0)
                    {
                        appointment.ExamApplications.Remove(applyItemList[i]);
                        if (appointment.ExamApplications.Count == 0)
                        {
                            // 移除预约
                            appointment.Pool.Current -= 1;
                            await _poolRepository.UpdateAsync(appointment.Pool);
                            await _appointmentRepository.DeleteAsync(appointment.Id);
                        }
                    }
                    applyItemList[i].Appointment = null;
                    applyItemList[i].AppointmentId = null;
                }
                await _examApplicationRepository.UpdateManyAsync(applyItemList);

                // 如果已确认需要回写his
                if (confirmed)
                {
                    List<ApplyExamItemStatusChangeDto> changeDtos = new List<ApplyExamItemStatusChangeDto>();
                    foreach (var applyItem in applyItemList)
                    { 
                        var dto = new ApplyExamItemStatusChangeDto();
                        dto.Name = applyItem.Name;
                        dto.PatientId = applyItem.PatientId;
                        dto.AppointmentInfo = null;
                        dto.ChangeType = ApplyExamItemStatusChangeType.Cancel;
                        dto.ChangeTypeStr = "取消申请项目预约";
                        dto.AppointmentNo = null;
                        dto.ApplyNumber = applyItem.ApplyNumber;
                        dto.ExamItemNumber = applyItem.ExamItemNumber;
                        dto.ExamItemCode = applyItem.ExamItemCode;
                        dto.ExamItemName = applyItem.ExamItemName;

                        changeDtos.Add(dto);
                    }

                    await _appointmentAppService.ApplyExamItemStatusChangeToHis(changeDtos);
                }

                res.code = 0;
                res.msg = "取消成功。";
            }
            catch (Exception e)
            {
                res.code = 1;
                res.msg = e.Message;
            }
            return res;
        }

        /// <summary>
        /// 确认方案
        /// </summary>
        public async Task<BackJson> ConfirmSolution(string applyNumbers)
        {
            var res = new BackJson();
            try
            {
                var applyNumberList = applyNumbers.Split().ToList();
                var list = await _examApplicationRepository.GetListAsync(e=>applyNumberList.Contains(e.ApplyNumber));
                var appointmentIds = list.DistinctBy(e => e.AppointmentId).Select(e => e.AppointmentId).ToList();
                var appointments = await _appointmentRepository.GetListAsync(e => appointmentIds.Contains(e.Id));
                for (var i = 0; i < appointments.Count; i++)
                {
                    appointments[i].Status = AppointmentStatus.Submit;
                }
                await _appointmentRepository.UpdateManyAsync(appointments);

                // 确认方案后回写his
                List<ApplyExamItemStatusChangeDto> changeDtos = new List<ApplyExamItemStatusChangeDto>();
                foreach (var appoint in appointments)
                {
                    foreach (var applyItem in appoint.ExamApplications)
                    {
                        var dto = new ApplyExamItemStatusChangeDto();
                        dto.Name = applyItem.Name;
                        dto.PatientId = applyItem.PatientId;
                        dto.AppointmentInfo = applyItem.AppointmentInfo;
                        dto.ChangeType = ApplyExamItemStatusChangeType.Submit;
                        dto.ChangeTypeStr = "确认预约方案";
                        dto.AppointmentNo = appoint.Number;
                        dto.ApplyNumber = applyItem.ApplyNumber;
                        dto.ExamItemNumber = applyItem.ExamItemNumber;
                        dto.ExamItemCode = applyItem.ExamItemCode;
                        dto.ExamItemName = applyItem.ExamItemName;

                        changeDtos.Add(dto);
                    }
                }
                await _appointmentAppService.ApplyExamItemStatusChangeToHis(changeDtos);
                res.code = 0;
                res.msg = "确认成功";
            }
            catch(Exception e)
            {
                res.code = 1;
                res.msg = e.Message;
            }
            return res;
        }
    }

}
